package org.mclll.xwangplugin.questTracking.listener;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import me.pikamug.quests.Quests;
import me.pikamug.quests.entity.BukkitCountableMob;
import me.pikamug.quests.events.quest.QuestQuitEvent;
import me.pikamug.quests.events.quester.BukkitQuesterPostChangeStageEvent;
import me.pikamug.quests.events.quester.BukkitQuesterPostCompleteQuestEvent;
import me.pikamug.quests.events.quester.BukkitQuesterPostStartQuestEvent;
import me.pikamug.quests.events.quester.BukkitQuesterPostUpdateObjectiveEvent;
import me.pikamug.quests.player.BukkitQuester;
import me.pikamug.quests.player.Quester;
import me.pikamug.quests.quests.Quest;
import me.pikamug.quests.quests.components.BukkitObjective;
import me.pikamug.quests.quests.components.Stage;
import org.bukkit.Bukkit;
import org.bukkit.DyeColor;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scoreboard.*;
import org.mclll.xwangplugin.MclllCustom;
import org.mclll.xwangplugin.log.LogUtil;
import org.mclll.xwangplugin.questTracking.persistence.Persistence.SerializeScore;
import org.mclll.xwangplugin.questTracking.questGoal.*;
import org.mclll.xwangplugin.util.GetChineseNameUtil;

import java.util.*;

import static org.mclll.xwangplugin.util.ScoreBoardUtil.copyToScore;
import static org.mclll.xwangplugin.util.ScoreBoardUtil.getScore;

/**
 * @author: geduo
 * @date: 2023/10/10 11:16
 */
public class QuestListener implements Listener {
    public static final String SCOREBOARD_NAME = "questTracking";
    private LogUtil log;
    private MclllCustom plugin;
    /**
     * 任务追踪积分板保存
     */
    public static Map<UUID, Map<String, List<SerializeScore>>> QUEST_SCOREBOARD_MAP = Maps.newHashMap();
    /**
     * 玩家当前追踪的任务
     */
    public static Map<UUID, String> PLAYER_CURRENT_QUESTS = Maps.newHashMap();
    /**
     * 玩家所有的任务进度
     */
    public static Map<UUID, Map<String, Integer>> PLAYER_QUEST_STAGE_MAP = Maps.newHashMap();


    public QuestListener(MclllCustom plugin) {
        this.plugin = plugin;
        log = plugin.getLog();
    }

    /**
     * 接任务自动将任务po在积分板
     *
     * @param event -
     */
    @EventHandler
    public void questTake(BukkitQuesterPostStartQuestEvent event) {
        BukkitQuester quester = event.getQuester();
        Player player = quester.getPlayer();
        Scoreboard newScoreboard = copyToScore(player.getScoreboard());
        player.setScoreboard(newScoreboard);
        Quest quest = event.getQuest();
        PLAYER_CURRENT_QUESTS.put(player.getUniqueId(), quest.getName());
        setPlayerStage(player.getUniqueId(), quest.getName(), 0);
        updateScoreBoard(quester, quest, quest.getStage(0));
//        LOG.info("-----questTake-----");
    }

    /**
     * 任务更新阶段触发更新积分板
     *
     * @param event -
     */
    @EventHandler
    public void updateQuest(BukkitQuesterPostChangeStageEvent event) {
        updateScoreBoard(event.getQuester(), event.getQuest(), event.getNextStage());
//        LOG.info("-----updateQuest-----" + event.getQuest().getRewards().getQuestPoints());
    }

    /**
     * 完成任务进度触发
     *
     * @param event -
     */
    @EventHandler
    public void finishGoal(BukkitQuesterPostUpdateObjectiveEvent event) {
        BukkitQuester quester = event.getQuester();
        Quest quest = event.getQuest();
        Player player = quester.getPlayer();
        Integer currentStageNum = quest.getStages().indexOf(quester.getCurrentStage(quest));
        if (currentStageNum == -1 || isNewStage(player.getUniqueId(), quest.getName(), currentStageNum)) {
            return;
        }
//        LOG.info("currentStageNum，{0}", currentStageNum);
        List<SerializeScore> scoreboard = QUEST_SCOREBOARD_MAP.get(player.getUniqueId()).get(quest.getName());
        if (scoreboard == null || scoreboard.isEmpty()) {
            return;
        }
        BukkitObjective questObj = (BukkitObjective) event.getObjective();
        switch (questObj.getType()) {
            case REACH_LOCATION:
                ReachLocationQuestTrack.goalFinish(event, scoreboard);
                break;
            case BREAK_BLOCK:
                BreakBlockQuestTrack.goalFinish(event, scoreboard);
                break;
            case KILL_MOB:
                MobKillQuestTrack.goalFinish(event, scoreboard);
                break;
            case TALK_TO_NPC:
                TalkToNpcQuestTrack.goalFinish(event, scoreboard);
                break;
            case PASSWORD:
                PasswordQuestTrack.goalFinish(event, scoreboard);
                break;
            case PLACE_BLOCK:
                PlaceBlockQuestTrack.goalFinish(event, scoreboard);
                break;
            case USE_BLOCK:
                UseBlockQuestTrack.goalFinish(event, scoreboard);
                break;
            case MILK_COW:
                MilkCowQuestTrack.goalFinish(event, scoreboard);
                break;
            case DELIVER_ITEM:
                DeliveryToNpcQuestTrack.goalFinish(event, scoreboard);
                break;
            case CATCH_FISH:
                CatchFishQuestTrack.goalFinish(event, scoreboard);
                break;
            case SHEAR_SHEEP:
                CutWoolQuestTrack.goalFinish(event, scoreboard);
                break;
            default:
        }
        scoreboardSave(player.getUniqueId(), event.getQuest().getName(), scoreboard);
        // 说明当前追踪的推进进度的任务
        if (quest.getName().equals(PLAYER_CURRENT_QUESTS.get(player.getUniqueId()))) {
            Scoreboard board = copyToScore(player.getScoreboard());
            showScore(board, scoreboard);
            player.setScoreboard(board);
        }
    }

    /**
     * 任务完成触发注销积分板
     *
     * @param event -
     */
    @EventHandler
    public void finishQuest(BukkitQuesterPostCompleteQuestEvent event) {
        Player player = event.getQuester().getPlayer();
        String questName = event.getQuest().getName();
        unRegScoreBoard(player, questName);
//        LOG.info("---------finish---------");
    }

    /**
     * 放弃任务注销积分板
     *
     * @param event -
     */
    @EventHandler
    public void quiteQuest(QuestQuitEvent event) {
        unRegScoreBoard(event.getQuester().getPlayer(), event.getQuest().getName());
    }

    /**
     * 玩家进入服务器有存储的任务追踪就恢复
     *
     * @param event -
     */
    @EventHandler(priority = EventPriority.MONITOR)
    public void onPlayerJoin(PlayerJoinEvent event) {
        fixQuestsTracking(event.getPlayer());
    }

    @EventHandler(priority = EventPriority.MONITOR)
    public void onPlayerChangeWorld(PlayerChangedWorldEvent event) {
        restoreQuestTracking(event.getPlayer());
    }

    private static void restoreQuestTracking(Player player) {
        Scoreboard playerScore = copyToScore(player.getScoreboard());
        player.setScoreboard(playerScore);
        String questName = PLAYER_CURRENT_QUESTS.get(player.getUniqueId());
        if (questName == null) {
            return;
        }
        List<SerializeScore> scoreboardLines = QUEST_SCOREBOARD_MAP.get(player.getUniqueId()).get(questName);
        if (scoreboardLines == null) {
            return;
        }
        showScore(playerScore, scoreboardLines);
    }

    private void updateScoreBoard(BukkitQuester quester, Quest quest, Stage questsStage) {
        Player player = quester.getPlayer();
        Scoreboard questTracking = copyToScore(player.getScoreboard());
        Objective context = questTracking.registerNewObjective(SCOREBOARD_NAME, Criteria.DUMMY, "进行中的任务", RenderType.HEARTS);
        context.setDisplaySlot(DisplaySlot.SIDEBAR);
        Integer line = 0;
        context.getScore(quest.getName()).setScore(line--);
        // 方块破坏类型
        BreakBlockQuestTrack.questTracking(context, line, questsStage);
        // 方块放置类型
        PlaceBlockQuestTrack.questTracking(context, line, questsStage);
        // 放块使用类型
        UseBlockQuestTrack.questTracking(context, line, questsStage);
        // 找地点类型
        ReachLocationQuestTrack.questTracking(context, line, questsStage);
        // 生物击杀类型
        MobKillQuestTrack.questTracking(context, line, questsStage);
        // Npc对话类型
        TalkToNpcQuestTrack.questTracking(context, line, questsStage);
        // 密码输入类型
        PasswordQuestTrack.questTracking(context, line, questsStage);
        // 挤牛奶类型
        MilkCowQuestTrack.questTracking(context, line, questsStage);
        // 捕鱼
        CatchFishQuestTrack.questTracking(context, line, questsStage);
        // npc交付类型
        DeliveryToNpcQuestTrack.questTracking(context, line, questsStage);
        // 剪羊毛
        CutWoolQuestTrack.questTracking(context, line, questsStage);
        scoreboardSave(player.getUniqueId(), quest.getName(), getScore(questTracking));
        // 说明当前追踪的推进进度的任务
        if (quest.getName().equals(PLAYER_CURRENT_QUESTS.get(player.getUniqueId()))) {
            player.setScoreboard(questTracking);
        }
    }

    public boolean isNewStage(UUID uniqueId, String questName, Integer currentStageNum) {
        Boolean ret = Optional.ofNullable(PLAYER_QUEST_STAGE_MAP.get(uniqueId))
                .map(map -> map.get(questName))
                .map(map -> !map.equals(currentStageNum))
                .orElse(true);
        setPlayerStage(uniqueId, questName, currentStageNum);
        return ret;
    }

    private void setPlayerStage(UUID uniqueId, String questName, Integer currentStageNum) {
        Map<String, Integer> data;
        if (PLAYER_QUEST_STAGE_MAP.containsKey(uniqueId)) {
            data = PLAYER_QUEST_STAGE_MAP.get(uniqueId);
        } else {
            data = Maps.newHashMap();
            PLAYER_QUEST_STAGE_MAP.put(uniqueId, data);
        }
        data.put(questName, currentStageNum);
    }

    private void unRegScoreBoard(Player player, String questName) {
        Scoreboard scoreboard = copyToScore(player.getScoreboard());
        Map<String, List<SerializeScore>> scoreData = QUEST_SCOREBOARD_MAP.get(player.getUniqueId());
//        player.setScoreboard(scoreboard);
        scoreData.remove(questName);
        PLAYER_QUEST_STAGE_MAP.get(player.getUniqueId()).remove(questName);
        String currentQuestName = PLAYER_CURRENT_QUESTS.get(player.getUniqueId());
        if (currentQuestName != null && currentQuestName.equals(questName)) {
            Iterator<String> iterator = scoreData.keySet().iterator();
            if (iterator.hasNext()) {
                List<SerializeScore> scoreList = scoreData.get(iterator.next());
                showScore(scoreboard, scoreList);
                player.setScoreboard(scoreboard);
                PLAYER_CURRENT_QUESTS.put(player.getUniqueId(), scoreList.get(0).getName());
            } else {
                PLAYER_CURRENT_QUESTS.remove(player.getUniqueId());
            }
            player.setScoreboard(scoreboard);
        }
    }

    private static void showScore(Scoreboard scoreboard, List<SerializeScore> scoreList) {
        Objective questTracking = scoreboard.registerNewObjective(SCOREBOARD_NAME, Criteria.DUMMY, "进行中的任务");
        questTracking.setDisplaySlot(DisplaySlot.SIDEBAR);
        for (SerializeScore score : scoreList) {
            questTracking.getScore(score.getName())
                    .setScore(score.getValue());
        }
    }

    private void scoreboardSave(UUID playId, String questName, List<SerializeScore> scoreList) {
        QUEST_SCOREBOARD_MAP.computeIfAbsent(playId, o -> Maps.newHashMap())
                .put(questName, scoreList);
    }

    public static void trackingOther(Player player, String questName) {
        UUID playerUuid = player.getUniqueId();
        Scoreboard newScoreBoard = copyToScore(player.getScoreboard());
        player.setScoreboard(newScoreBoard);
        if (questName.equals(PLAYER_CURRENT_QUESTS.get(playerUuid))) {
            PLAYER_CURRENT_QUESTS.put(playerUuid, "");
            return;
        }
        Map<String, List<SerializeScore>> playerQuestsMap = QUEST_SCOREBOARD_MAP.get(playerUuid);
        if (playerQuestsMap == null || playerQuestsMap.isEmpty()) {
            return;
        }
        List<SerializeScore> score = playerQuestsMap.get(questName);
        if (score == null || score.isEmpty()) {
            return;
        }
        PLAYER_CURRENT_QUESTS.put(playerUuid, questName);
        showScore(newScoreBoard, score);
    }

    private static String synchronizeQuestsData(BukkitObjective questObj) {
        boolean isFinish = questObj.getGoal() - questObj.getProgress() == 0;
        String message = questObj.getMessage();
        switch (questObj.getType()) {
            case REACH_LOCATION:
                String locationName = message.substring(message.lastIndexOf(" ") + 1);
                return isFinish ? ReachLocationQuestTrack.getFinishFormat(locationName) :
                        ReachLocationQuestTrack.getGoalFormat(locationName);
            case BREAK_BLOCK:
                return isFinish ? BreakBlockQuestTrack.getFinishFormat((ItemStack) questObj.getGoalObject()) :
                        BreakBlockQuestTrack.getGoalFormat((ItemStack) questObj.getGoalObject(), questObj.getProgress(), questObj.getGoal());
            case KILL_MOB:
                return isFinish ? MobKillQuestTrack.getFinishFormat(((BukkitCountableMob)questObj.getGoalObject()).getEntityType()) :
                        MobKillQuestTrack.getGoalFormat(((BukkitCountableMob)questObj.getGoalObject()).getEntityType(), questObj.getProgress(), questObj.getGoal());
            case TALK_TO_NPC:
                String talkNpcName = message.substring(message.indexOf("to ") + 3);
                return isFinish ? TalkToNpcQuestTrack.getFinishFormat(talkNpcName) :
                        TalkToNpcQuestTrack.getGoalFormat(talkNpcName);
            case PASSWORD:
                return isFinish ? PasswordQuestTrack.getFinishFormat(questObj.getMessage().substring(2)) :
                        PasswordQuestTrack.getGoalFormat(questObj.getMessage().substring(2));
            case PLACE_BLOCK:
                return isFinish ? PlaceBlockQuestTrack.getFinishFormat((ItemStack) questObj.getGoalObject()) :
                        PlaceBlockQuestTrack.getGoalFormat((ItemStack) questObj.getGoalObject(), questObj.getProgress(), questObj.getGoal());
            case USE_BLOCK:
                return isFinish ? UseBlockQuestTrack.getFinishFormat((ItemStack) questObj.getGoalObject()) :
                        UseBlockQuestTrack.getGoalFormat((ItemStack) questObj.getGoalObject(), questObj.getProgress(), questObj.getGoal());
            case MILK_COW:
                return isFinish ? MilkCowQuestTrack.getFinishFormat() :
                        MilkCowQuestTrack.getGoalFormat(questObj.getProgress(), questObj.getGoal());
            case DELIVER_ITEM:
                String deliverNpcName = message.substring(message.indexOf("to ") + 3, (message.indexOf(":")));
                ItemStack goalObject = (ItemStack) questObj.getGoalObject();
                ItemStack progressObj = (ItemStack) questObj.getProgressObject();
                String itemName = GetChineseNameUtil.get(goalObject.getType());
                return isFinish ? DeliveryToNpcQuestTrack.getFinishFormat(deliverNpcName, itemName) :
                        DeliveryToNpcQuestTrack.getGoalFormat(deliverNpcName, itemName, goalObject.getAmount() - progressObj.getAmount());
            case CATCH_FISH:
                return isFinish ? CatchFishQuestTrack.getFinishFormat() :
                        CatchFishQuestTrack.getGoalFormat(questObj.getProgress(), questObj.getGoal());
            case SHEAR_SHEEP:
                String color = message.substring(8, message.indexOf(" ", 8));
                DyeColor dyeColor = DyeColor.valueOf(color.toUpperCase(Locale.ROOT));
                return isFinish ? CutWoolQuestTrack.getFinishFormat(dyeColor) :
                        CutWoolQuestTrack.getGoalFormat(dyeColor, questObj.getProgress(), questObj.getGoal());
            default:
                return "";
        }
    }

    public static void fixQuestsTracking(Player player) {
        Quests quests = (Quests) Bukkit.getPluginManager().getPlugin("Quests");
        UUID uniqueId = player.getUniqueId();
        Quester quester = quests.getQuester(uniqueId);
        Map<String, Quest> trueQuesMap = Maps.newHashMap();

        for (Quest k : quester.getCurrentQuests().keySet()) {
            trueQuesMap.put(k.getName(), k);
        }
        Map<String, List<SerializeScore>> scoreBoardData = QUEST_SCOREBOARD_MAP.computeIfAbsent(uniqueId, k -> Maps.newHashMap());
        Map<String, Integer> questStageData = PLAYER_QUEST_STAGE_MAP.computeIfAbsent(uniqueId, k -> Maps.newHashMap());
        // 清理无效任务
        List<String> keySet = new ArrayList<String>(scoreBoardData.keySet());
        for (String s : keySet) {
            if (!trueQuesMap.containsKey(s)) {
                scoreBoardData.remove(s);
                questStageData.remove(s);
            }
        }
        // 同步任务
        for (Quest quest : trueQuesMap.values()) {
            String questName = quest.getName();
            if (scoreBoardData.containsKey(questName)) {

            } else {
                LinkedList<me.pikamug.quests.quests.components.Objective> currentObjectives = quester.getCurrentObjectives(quest, true, true);
                List<SerializeScore> serializeScores = Lists.newArrayList();
                questStageData.put(questName, quest.getStages().indexOf(quester.getCurrentStage(quest)));
                int line = 0;
                serializeScores.add(new SerializeScore(questName, line--));
                for (me.pikamug.quests.quests.components.Objective currentObjective : currentObjectives) {
                    serializeScores.add(new SerializeScore(synchronizeQuestsData((BukkitObjective) currentObjective), line--));
                }
                scoreBoardData.put(questName, serializeScores);
            }
        }
        String currentQuestName = PLAYER_CURRENT_QUESTS.get(uniqueId);
        if (trueQuesMap.containsKey(currentQuestName)) {
            restoreQuestTracking(player);
        } else {
            PLAYER_CURRENT_QUESTS.remove(uniqueId);
        }
    }

}

